﻿using GC_RF.DataAccess.Abstractions.Interfaces.Helpers;
using GC_RF.DataAccess.Abstractions.Interfaces.Repositories;
using System;
using System.Collections.Generic;
using System.Data.Entity.Core.Objects;
using System.Linq;
using System.Linq.Expressions;

namespace GC_RF.DataAccess.Abstractions.Repositories
{
	public abstract class BaseRepository<TEntity> : IRepository<TEntity>
		where TEntity : class
	{
		#region Variables

		private readonly ObjectContext _context;
		private object _objSet;
		private readonly object _synchObj = new object();

		#endregion

		#region Constructors

		protected BaseRepository(IUnitOfWork unitOfWork)
		{
			if (unitOfWork == null)
				throw new ArgumentNullException(string.Format("unitOfWork"));

			_context = unitOfWork.Context;
			if (_context == null)
				throw new ArgumentNullException(string.Format("context"));
		}

		#endregion

		#region IRepository Members

		public void Add(TEntity entity)
		{
			GetObjectSet<TEntity>().AddObject(entity);
		}

		public void Delete(TEntity entity)
		{
			GetObjectSet<TEntity>().DeleteObject(entity);
		}

		public int Count()
		{
			return GetObjectSet<TEntity>().Count();
		}

		public IEnumerable<TEntity> Query(Expression<Func<TEntity, bool>> filter)
		{
			return GetObjectSet<TEntity>().Where(filter);
		}

		public IEnumerable<TD> TakeQuery<TD>(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderFunc, int skipCount, int takeCount, Expression<Func<TEntity, TD>> func)
		{
			IQueryable<TEntity> result = null;
			if (orderFunc != null)
				result = orderFunc(GetObjectSet<TEntity>());
			return (result ?? GetObjectSet<TEntity>()).Skip(skipCount).Take(takeCount).Select(func);
		}

		public IEnumerable<TD> GetData<TD>(Expression<Func<TEntity, TD>> func)
		{
			return GetObjectSet<TEntity>().Select(func);
		}

		public IEnumerable<TD> GetData<TD>(Expression<Func<TEntity, TD>> func, Expression<Func<TEntity, bool>> filter)
		{
			return GetObjectSet<TEntity>().Where(filter).Select(func);
		}

		#endregion

		#region Internal Implementaion

		protected virtual IObjectSet<TEntity> GetObjectSet<TEntity>()
			where TEntity : class
		{
			if (_objSet != null)
				return (IObjectSet<TEntity>)_objSet;

			lock (_synchObj)
			{
				if (_objSet != null)
					return (IObjectSet<TEntity>)_objSet;

				_objSet = _context.CreateObjectSet<TEntity>();
			}

			return (IObjectSet<TEntity>)_objSet;
		}

		#endregion
	}
}
